﻿#include "../../includes/common/packagemanager.h"
#include "../../includes/common/common.h"
#include "../../includes/QsLog/QsLog.h"

#include <QFileInfo>

CPackageManager::CPackageManager(QObject *parent)
    : QObject(parent),
      m_currentPro(0),
      m_totalPro(0)
{

}

CPackageManager::~CPackageManager()
{
    m_unpackfile.close();
}

/**
 * @brief CPackageManager::clearalldatas 清除所有数据
 */
void CPackageManager::clearalldatas(void)
{
    if(m_unpackfile.isOpen())
        m_unpackfile.close();

    m_currentPro=m_totalPro=0;
    m_currentOperLog = "";
    memset(&m_PackFileHearder,0,sizeof(tagPackFileHearder));
    m_PackFileChunks.clear();
    m_PackFileChunkList.clear();
}

/**
 * @brief CPackageManager::packagingfile 开始打包文件
 * @param decpath
 * @return
 */
bool CPackageManager::packagingfile(QString decpath)
{
    QFile pdecFile(decpath);
    if(!pdecFile.open(QIODevice::WriteOnly))
    {
        QLOG_ERROR()<<decpath+QString::fromLocal8Bit("打开失败!");
        return false;
    }

    m_currentPro = 0;
    m_totalPro = m_PackFileChunks.size();

    emit signalWorkingProcess(m_currentPro,m_totalPro,decpath+QString::fromLocal8Bit("开始写文件数据..."));
    QLOG_INFO()<<decpath+QString::fromLocal8Bit("开始写文件数据...");

    pdecFile.write((const char*)&m_PackFileHearder,sizeof(tagPackFileHearder));

    for(int index=0;index<m_PackFileChunkList.size();index++)
    {
        pdecFile.write((const char*)&m_PackFileChunkList[index],sizeof(tagPackFileChunk));

        m_currentPro = index;
        m_currentOperLog = QString::fromLocal8Bit("当前写文件数据:")+m_PackFileChunkList[index].filepath;

        emit signalWorkingProcess(m_currentPro,m_totalPro,m_currentOperLog);
        QLOG_INFO()<<m_currentOperLog;

        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("写文件数据完成."));
    QLOG_INFO()<<QString::fromLocal8Bit("写文件数据完成.");

    m_currentPro = 0;
    m_totalPro = m_PackFileChunks.size();

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("开始压缩文件..."));
    QLOG_INFO()<<QString::fromLocal8Bit("开始压缩文件...");

    // 开始写文件数据
    for(int index=0;index<m_PackFileChunkList.size();index++)
    {
        QFile pdataFile(m_currentWorkingDir+"/"+m_PackFileChunkList[index].filepath);
        if(!pdataFile.open(QIODevice::ReadOnly))
        {
            QLOG_ERROR()<<m_currentWorkingDir+"/"+m_PackFileChunkList[index].filepath+
                          QString::fromLocal8Bit("打开失败!");
            return false;
        }

        pdecFile.write(qCompress(pdataFile.readAll()));

        pdataFile.close();

        m_currentPro = index;
        m_currentOperLog = QString::fromLocal8Bit("当前压缩文件:")+m_PackFileChunkList[index].filepath;

        emit signalWorkingProcess(m_currentPro,m_totalPro,m_currentOperLog);
        QLOG_INFO()<<m_currentOperLog;

        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }

    pdecFile.close();

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("压缩文件完成."));
    QLOG_INFO()<<QString::fromLocal8Bit("压缩文件完成.");

    return true;
}

/**
 * @brief CPackageManager::packagingFile 打包文件到包中
 * @param filepaths
 * @param decpath
 * @return
 */
bool CPackageManager::packagingFile(QVector<QString> filepaths,QString decpath)
{
    if(filepaths.isEmpty() || decpath == "" || m_currentWorkingDir == "")
    {
        emit signalWorkingProcess(m_currentPro,
                                  m_totalPro,
                                  QString::fromLocal8Bit("参数不能为空:")+
                                  decpath+
                                  " "+
                                  m_currentWorkingDir);
        QLOG_ERROR()<<QString::fromLocal8Bit("参数不能为空:")+
                      decpath+
                      " "+
                      m_currentWorkingDir;
        return false;
    }

    QMutexLocker locker(&m_MutexFile);

    // 重置所有的数据
    clearalldatas();

    m_PackFileHearder.mark[0] = 'P';
    m_PackFileHearder.mark[1] = 'C';
    m_PackFileHearder.mark[2] = 'K';

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("开始解析文件..."));
    QLOG_INFO()<<QString::fromLocal8Bit("开始解析文件...");

    QVector<QString> tmpfilepaths;

    for(int i=0;i<filepaths.size();i++)
    {
        QFileInfo pfileinfo(filepaths[i]);
        if(!pfileinfo.exists())
        {
            QLOG_WARN()<<filepaths[i]+QString::fromLocal8Bit("不存在.");
            continue;
        }

        if(pfileinfo.isFile())
        {
            tmpfilepaths.push_back(filepaths[i]);
        }
        else if(pfileinfo.isDir())
        {
            QVector<QString> pfilelist;
            if(FindFile(filepaths[i],pfilelist) >= 0 && !pfilelist.isEmpty())
            {
                tmpfilepaths += pfilelist;
            }
        }
    }

    m_PackFileHearder.fileCount = tmpfilepaths.size();
    packagingFileData(tmpfilepaths,decpath);

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("解析文件完成."));
    QLOG_INFO()<<QString::fromLocal8Bit("解析文件完成.");

    m_totalPro = m_PackFileHearder.fileCount;

    return packagingfile(decpath);
}

/**
 * @brief CPackageManager::getfilefrompackage 得到压缩包中指定的文件
 * @param filename
 * @param filedata
 * @return
 */
bool CPackageManager::getfilefrompackage(QString filename,QByteArray& filedata)
{
    if(filename == "" || !m_unpackfile.isOpen())
    {
        QLOG_ERROR()<<QString::fromLocal8Bit("当前压缩包没有打开或者文件名为空.");
        return false;
    }

    QHash<QString,tagPackFileChunk>::iterator iter = m_PackFileChunks.find(filename);
    if(iter == m_PackFileChunks.end())
    {
        QLOG_ERROR()<<filename+QString::fromLocal8Bit("不在当前压缩包中.");
        return false;
    }

    filedata.clear();

    m_unpackfile.seek(iter.value().startpos);
    filedata = qUncompress(m_unpackfile.read(iter.value().zipfilesize));

    return filedata.isEmpty() ? false : true;
}

/**
 * @brief CPackageManager::editfilefrompakage 编辑压缩包中指定的文件
 * @param filename
 * @param filedata
 * @return
 */
bool CPackageManager::editfilefrompakage(QString filename,QByteArray filedata)
{
    if(filename == "" || !m_unpackfile.isOpen() || filedata.isEmpty())
    {
        QLOG_ERROR()<<QString::fromLocal8Bit("当前压缩包没有打开,文件名为空或者文件没有数据.");
        return false;
    }

    QHash<QString,tagPackFileChunk>::iterator iter = m_PackFileChunks.find(filename);
    if(iter == m_PackFileChunks.end())
    {
        QLOG_ERROR()<<filename+QString::fromLocal8Bit("不在当前压缩包中.");
        return false;
    }

    m_currentPro = m_totalPro = 0;
    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("开始压缩文件..."));
    QLOG_INFO()<<QString::fromLocal8Bit("开始压缩文件...");

    QByteArray zipData = qCompress(filedata);

    iter.value().srcfilesize = filedata.size();
    iter.value().zipfilesize = zipData.size();

    QFile pdecFile(m_currentWorkingDir+"/template.pak");
    if(!pdecFile.open(QIODevice::ReadWrite))
    {
        emit signalWorkingProcess(m_currentPro,m_totalPro,
                                  m_currentWorkingDir+"/template.pak"+
                                  QString::fromLocal8Bit("创建失败."));
        QLOG_ERROR()<<m_currentWorkingDir+"/template.pak"+
                      QString::fromLocal8Bit("创建失败.");
        return false;
    }

    QMap<int,tagPackFileChunk> ppPackFileChunks;
    iter = m_PackFileChunks.begin();
    for(;iter != m_PackFileChunks.end();++iter)
    {
        ppPackFileChunks[iter.value().fileIndex] = iter.value();
    }

    bool isStartComputer = false;
    tagPackFileChunk lasttagPackFileChunk;
    QMap<int,tagPackFileChunk> ptmpPackFileChunks;
    QMap<int,tagPackFileChunk>::iterator itertwo = ppPackFileChunks.begin();
    for(;itertwo != ppPackFileChunks.end();++itertwo)
    {
        qint32 tmpPosition = itertwo.value().startpos;

        if(!isStartComputer && itertwo.value().filepath == filename)
        {
            isStartComputer = true;
        }

        if(isStartComputer && itertwo.value().filepath != filename)
        {
            tmpPosition = lasttagPackFileChunk.startpos + lasttagPackFileChunk.zipfilesize;
        }

        lasttagPackFileChunk = tagPackFileChunk(itertwo.value().filepath,
                                                tmpPosition,
                                                itertwo.value().srcfilesize,
                                                itertwo.value().zipfilesize);
        ptmpPackFileChunks[itertwo.key()] = lasttagPackFileChunk;

    }

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("写文件头..."));
    QLOG_INFO()<<QString::fromLocal8Bit("写文件头...");

    if(pdecFile.write((const char*)&m_PackFileHearder,sizeof(tagPackFileHearder)) != sizeof(tagPackFileHearder))
    {
        QLOG_ERROR()<<QString::fromLocal8Bit("写tagPackFileHearder失败.");
    }

    itertwo = ptmpPackFileChunks.begin();
    for(;itertwo != ptmpPackFileChunks.end();++itertwo)
    {
        if(pdecFile.write((const char*)&itertwo.value(),sizeof(tagPackFileChunk)) != sizeof(tagPackFileChunk))
        {
            QLOG_ERROR()<<QString::fromLocal8Bit("写tagPackFileChunk失败.");
        }
    }

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("写文件数据..."));
    QLOG_INFO()<<QString::fromLocal8Bit("写文件数据...");

    m_totalPro = ptmpPackFileChunks.size();
    itertwo = ptmpPackFileChunks.begin();
    for(;itertwo != ptmpPackFileChunks.end();++itertwo)
    {
        m_currentPro++;
        emit signalWorkingProcess(m_currentPro,m_totalPro,
                                  QString::fromLocal8Bit("处理文件:")+
                                  itertwo.value().filepath);
        QLOG_INFO()<<QString::fromLocal8Bit("处理文件:")+
                     itertwo.value().filepath;

        if(itertwo.value().filepath == filename)
        {
            if(pdecFile.write(zipData) != zipData.size())
            {
                emit signalWorkingProcess(m_currentPro,m_totalPro,
                                          QString::fromLocal8Bit("写文件：")+
                                          itertwo.value().filepath+
                                          QString::fromLocal8Bit("失败."));
                QLOG_ERROR()<<QString::fromLocal8Bit("写文件：")+
                              itertwo.value().filepath+
                              QString::fromLocal8Bit("失败.");
            }
        }
        else
        {
            QHash<QString,tagPackFileChunk>::iterator iter = m_PackFileChunks.find(itertwo.value().filepath);
            if(iter != m_PackFileChunks.end())
            {
                m_unpackfile.seek(iter.value().startpos);
                QByteArray tmpData = m_unpackfile.read(iter.value().zipfilesize);
                if(tmpData.size() != iter.value().zipfilesize)
                {
                    emit signalWorkingProcess(m_currentPro,m_totalPro,
                                              QString::fromLocal8Bit("写文件：")+
                                              itertwo.value().filepath+
                                              QString::fromLocal8Bit("失败1."));
                    QLOG_ERROR()<<QString::fromLocal8Bit("写文件：")+
                                  itertwo.value().filepath+
                                  QString::fromLocal8Bit("失败1.");
                }
                else
                {
                    if(pdecFile.write(tmpData) != tmpData.size())
                    {
                        emit signalWorkingProcess(m_currentPro,m_totalPro,
                                                  QString::fromLocal8Bit("写文件：")+
                                                  itertwo.value().filepath+
                                                  QString::fromLocal8Bit("失败3."));
                        QLOG_ERROR()<<QString::fromLocal8Bit("写文件：")+
                                      itertwo.value().filepath+
                                      QString::fromLocal8Bit("失败3.");
                    }
                }
            }
        }
    }

    // 覆盖原有文件
    m_unpackfile.reset();
    m_unpackfile.seek(0);
    pdecFile.seek(0);

    if(m_unpackfile.write(pdecFile.readAll()) <= 0)
    {
        emit signalWorkingProcess(m_currentPro,m_totalPro,
                                  m_unpackfile.fileName()+
                                  QString::fromLocal8Bit("覆盖失败."));
        QLOG_INFO()<<m_unpackfile.fileName()+
                     QString::fromLocal8Bit("覆盖失败.");
    }

    pdecFile.close();

    DeleteTheFileOrFolder(m_currentWorkingDir+"/template.pak");

    if(m_PackFileChunkList.size() == ptmpPackFileChunks.size())
    {
        itertwo = ptmpPackFileChunks.begin();
        for(;itertwo != ptmpPackFileChunks.end();++itertwo)
        {
            QHash<QString,tagPackFileChunk>::iterator iter = m_PackFileChunks.find(itertwo.value().filepath);
            if(iter != m_PackFileChunks.end())
            {
                iter.value() = itertwo.value();
            }

            m_PackFileChunkList[itertwo.value().fileIndex] = itertwo.value();
        }
    }

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("压缩文件完成."));
    QLOG_INFO()<<QString::fromLocal8Bit("压缩文件完成.");

    return true;
}

/**
 * @brief CPackageManager::getAllpackageFileSize 得到压缩包文件长度
 * @return
 */
qint64 CPackageManager::getAllpackageFileSize(void)
{
    qint64 totalSize = 0;

    QHash<QString,tagPackFileChunk>::iterator iter = m_PackFileChunks.begin();
    for(;iter != m_PackFileChunks.end();++iter)
    {
        totalSize += iter.value().zipfilesize;
    }

    return totalSize;
}

/**
 * @brief CPackageManager::packagingFileData 打包文件到包中
 * @param filepaths
 * @param decpath
 * @return
 */
bool CPackageManager::packagingFileData(QVector<QString> filepaths,QString decpath)
{
    if(filepaths.isEmpty() || decpath.isEmpty())
    {
        QLOG_ERROR()<<QString::fromLocal8Bit("文件或者目标目录不存在.");
        return false;
    }

    qint64 tmpStartpos = sizeof(tagPackFileHearder)+sizeof(tagPackFileChunk)*filepaths.size();

    for(int i=0;i<filepaths.size();i++)
    {
        QFile pdataFile(filepaths[i]);
        if(!pdataFile.open(QIODevice::ReadOnly))
        {
            QLOG_ERROR()<<QString::fromLocal8Bit("打开文件")+
                          filepaths[i]+
                          QString::fromLocal8Bit("失败.");
            continue;
        }

        QString tmpDecDir = filepaths[i];
        int pos = filepaths[i].lastIndexOf(m_currentWorkingDir);
        if(pos >= 0)
        {
            tmpDecDir = filepaths[i].mid(pos+m_currentWorkingDir.size()+1);
        }

        tagPackFileChunk pPackFileChunk;
        strcpy(pPackFileChunk.filepath,tmpDecDir.toStdString().c_str());

        pPackFileChunk.srcfilesize = pdataFile.size();
        pPackFileChunk.zipfilesize = qCompress(pdataFile.readAll()).size();
        pPackFileChunk.startpos = tmpStartpos+getAllpackageFileSize();
        pPackFileChunk.fileIndex = m_PackFileChunkList.size();

        m_PackFileChunks[filepaths[i]] = pPackFileChunk;
        m_PackFileChunkList.push_back(pPackFileChunk);

        pdataFile.close();

        m_currentPro += 1;
        m_currentOperLog = QString::fromLocal8Bit("当前正在解析文件:")+filepaths[i];

        emit signalWorkingProcess(m_currentPro,m_totalPro,m_currentOperLog);
        QLOG_INFO()<<m_currentOperLog;

        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }

    return true;
}

/**
 * @brief CPackageManager::unpackagingsinglefile 解压单个文件
 * @param filepath
 * @return
 */
bool CPackageManager::unpackagingsinglefile(QString filepath)
{
    if(filepath == "" || m_currentWorkingDir == "" || !m_unpackfile.isOpen())
    {
        QLOG_ERROR()<<QString::fromLocal8Bit("文件,目标目录不存在或者压缩包没有打开.");
        return false;
    }

    /*emit signalWorkingProcess(m_currentPro,
                              m_totalPro,
                              QString::fromLocal8Bit("解压文件:")+filepath);*/

    QHash<QString,tagPackFileChunk>::iterator iter = m_PackFileChunks.find(filepath);
    if(iter == m_PackFileChunks.end())
    {
        emit signalWorkingProcess(m_currentPro,
                                  m_totalPro,
                                  filepath+QString::fromLocal8Bit("不存在."));
        QLOG_ERROR()<<filepath+QString::fromLocal8Bit("不存在.");
        return false;
    }

    QString pdecFile = m_currentWorkingDir + "/" + iter.value().filepath;
    int pos = pdecFile.lastIndexOf("/");
    if(pos > 0)
    {
        CreateTheFileOrFolder(pdecFile.mid(0,pos),1);
    }

    // 获取压缩数据
    m_unpackfile.seek(iter.value().startpos);
    QByteArray tmpFileData = m_unpackfile.read(iter.value().zipfilesize);
    if(tmpFileData.size() != iter.value().zipfilesize)
    {
        emit signalWorkingProcess(m_currentPro,
                                  m_totalPro,
                                  filepath+
                                  QString::fromLocal8Bit(":获取的压缩数据长度不对.")+
                                  QString::asprintf("%ld %lld",tmpFileData.size(),iter.value().zipfilesize));
        QLOG_ERROR()<<filepath+
                      QString::fromLocal8Bit(":获取的压缩数据长度不对.")+
                      QString::asprintf("%ld %lld",tmpFileData.size(),iter.value().zipfilesize);
        return false;
    }

    // 解压数据
    QByteArray tmpunZipFileData = qUncompress(tmpFileData);
    if(tmpunZipFileData.size() != iter.value().srcfilesize)
    {
        emit signalWorkingProcess(m_currentPro,
                                  m_totalPro,
                                  filepath+
                                  QString::fromLocal8Bit(":获取的解压数据长度不对.")+
                                  QString::asprintf("%ld %lld",tmpunZipFileData.size(),iter.value().srcfilesize));
        QLOG_ERROR()<<filepath+
                      QString::fromLocal8Bit(":获取的解压数据长度不对.")+
                      QString::asprintf("%ld %lld",tmpunZipFileData.size(),iter.value().srcfilesize);
        return false;
    }

    // 开始写文件
    QFile pwritefile(pdecFile);
    if(!pwritefile.open(QIODevice::WriteOnly))
    {
        emit signalWorkingProcess(m_currentPro,
                                  m_totalPro,
                                  pdecFile+QString::fromLocal8Bit("建立失败."));
        QLOG_ERROR()<<pdecFile+QString::fromLocal8Bit("建立失败.");
        return false;
    }

    if(pwritefile.write(tmpunZipFileData) != tmpunZipFileData.size())
    {
        emit signalWorkingProcess(m_currentPro,
                                  m_totalPro,
                                  pdecFile+QString::fromLocal8Bit("数据写入长度不对."));
        QLOG_ERROR()<<pdecFile+QString::fromLocal8Bit("数据写入长度不对.");
        return false;
    }

    pwritefile.close();

    emit signalWorkingProcess(m_currentPro,
                              m_totalPro,
                              filepath+QString::fromLocal8Bit("解压完成."));
    QLOG_INFO()<<filepath+QString::fromLocal8Bit("解压完成.");

    m_currentPro++;

    return true;
}

/**
 * @brief CPackageManager::unpackagingFile 打开一个文件包
 * @param decpakpath
 * @return
 */
bool CPackageManager::unpackagingFile(QString decpakpath)
{
    if(decpakpath == "" || m_currentWorkingDir == "")
    {
        QLOG_ERROR()<<QString::fromLocal8Bit("文件或者目标目录不存在.");
        return false;
    }

    QMutexLocker locker(&m_MutexFile);

    // 重置所有的数据
    clearalldatas();

    m_unpackfile.setFileName(decpakpath);
    if(!m_unpackfile.open(QIODevice::ReadWrite))
    {
        QLOG_ERROR()<<decpakpath<<QString::fromLocal8Bit("打开不成功.");
        return false;
    }

    // 读取文件头
    if(m_unpackfile.read((char*)(&m_PackFileHearder),sizeof(tagPackFileHearder)) < sizeof(tagPackFileHearder))
    {
        QLOG_ERROR()<<decpakpath<<QString::fromLocal8Bit("读取不成功.");
        return false;
    }

    if(m_PackFileHearder.mark[0] != 'P' ||
       m_PackFileHearder.mark[1] != 'C' ||
       m_PackFileHearder.mark[2] != 'K')
    {
        emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("文件格式不对，无法解析当前文件."));
        QLOG_ERROR()<<QString::fromLocal8Bit("文件格式不对，无法解析当前文件.");
        return false;
    }

    m_totalPro = m_PackFileHearder.fileCount;
    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("开始解析文件..."));
    QLOG_INFO()<<QString::fromLocal8Bit("开始解析文件...");

    for(int i=0;i<m_PackFileHearder.fileCount;i++)
    {
        tagPackFileChunk pPackFileChunk;
        if(m_unpackfile.read((char*)&pPackFileChunk,sizeof(tagPackFileChunk)) < sizeof(tagPackFileChunk))
        {
            QLOG_ERROR()<<QString::fromLocal8Bit("压缩包读取失败.");
            return false;
        }

        m_PackFileChunks[pPackFileChunk.filepath] = pPackFileChunk;

        m_currentPro++;
        emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("解析到文件:")+pPackFileChunk.filepath);
        QLOG_INFO()<<QString::fromLocal8Bit("解析到文件:")+pPackFileChunk.filepath;
    }

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("解析文件完成."));
    QLOG_INFO()<<QString::fromLocal8Bit("解析文件完成.");

    m_currentPro = 0;
    m_totalPro = m_PackFileChunks.size();

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("开始解压文件."));
    QLOG_INFO()<<QString::fromLocal8Bit("开始解压文件.");

    QHash<QString,tagPackFileChunk>::iterator itertwo = m_PackFileChunks.begin();
    for(;itertwo != m_PackFileChunks.end();++itertwo)
    {
        if(!unpackagingsinglefile(itertwo.key()))
        {
            emit signalWorkingProcess(m_currentPro,m_totalPro,
                                      QString::fromLocal8Bit("解压文件:")+
                                      itertwo.key()+
                                      QString::fromLocal8Bit("发生错误."));
            QLOG_ERROR()<<QString::fromLocal8Bit("解压文件:")+
                          itertwo.key()+
                          QString::fromLocal8Bit("发生错误.");
        }
    }

    emit signalWorkingProcess(m_currentPro,m_totalPro,QString::fromLocal8Bit("解压文件完成."));
    QLOG_INFO()<<QString::fromLocal8Bit("解压文件完成.");

    return true;
}
